﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;

namespace Proyecto_XNA
{
    class Player
    {
        Sprite kSprite;

        Vector2 vPosition;
        Texture2D kTexture;
        Rectangle kPlayableRect;

        private Animation kIdle;
        private Animation kDying;
        private Animation kJumping;
        private Animation kWalking;

        private SpriteEffects kFlipSprite = SpriteEffects.None;

        private SoundEffect killedSound;
        private SoundEffect jumpSound;
        private SoundEffect walkingSound;

        private AnimationPlayer kAnimationPlayer;

        private bool isJumping;
        private bool wasJumping;

        private float jumpTime;

        private const float MoveAcceleration = 10000.0f;
        private const float MaxMoveSpeed = 150.0f;
        private const float GroundDragFactor = 0.58f;
        private const float AirDragFactor = 0.65f;

        private const float MaxJumpTime = 0.35f;
        private const float JumpLaunchVelocity = -4000.0f;
        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 600.0f;
        private const float JumpControlPower = 0.14f;

        bool isOnGround;

        Level lLevel;

        public Level Level
        {
            get { return lLevel; }
        }

        bool isAlive;

        public bool IsAlive
        {
            get { return isAlive; }
        }

        public bool IsOnGround
        {
            get { return isOnGround; }
            set { isOnGround = value; }
        }

        Vector2 position;

        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }

        Vector2 velocity;

        public Vector2 Velocity
        {
            get { return velocity; }
            set { velocity = value; }
        }

        private Rectangle localBounds;

        private float movement;

        public Rectangle BoundingRectangle
        {
            get
            {
                int left = (int)Math.Round(Position.X - kAnimationPlayer.Origin.X) + localBounds.X;
                int top = (int)Math.Round(Position.Y - kAnimationPlayer.Origin.Y) + localBounds.Y;

                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
            }
        }

        public Player(Level lLevel, Vector2 vPosition)
        {
            this.lLevel = lLevel;

            LoadContent();

            Reset(vPosition);
        }

        public void LoadContent()
        {
            // Load animated textures.
            kIdle = new Animation(Level.Content.Load<Texture2D>("PlayerSprites/IdleState"), 
                0.1f, true);
            kDying = new Animation(Level.Content.Load<Texture2D>("PlayerSprites/Player Death"),
                0.1f, true);
            kJumping = new Animation(Level.Content.Load<Texture2D>("PlayerSprites/JumpState"), 
                0.1f, false);
            kWalking = new Animation(Level.Content.Load<Texture2D>("PlayerSprites/WalkState"), 
                0.1f, true);

            // Calculate bounds within texture size.
            int width = (int)(kIdle.FrameWidth * 0.4);
            int left = (kIdle.FrameWidth - width) / 2;
            int height = (int)(kIdle.FrameWidth * 0.8);
            int top = kIdle.FrameHeight - height;

            localBounds = new Rectangle(left, top, width, height);

            // Load sounds.
            //killedSound = Level.Content.Load<SoundEffect>("Sounds/PlayerKilled");
            //jumpSound = Level.Content.Load<SoundEffect>("Sounds/PlayerJump");
        }

        public void Reset(Vector2 position)
        {
            Position = position;
            Velocity = Vector2.Zero;
            isAlive = true;

            kAnimationPlayer.PlayAnimation(kIdle);
        }

        private void GetInput()
        {
            // Get input state.
            KeyboardState keyboardState = Keyboard.GetState();

            // Ignore small movements to prevent running in place.
            if (Math.Abs(movement) < 0.5f)
            {
                movement = 0.0f;
            }

            // If any digital horizontal movement input is found, override the analog movement.
            if (keyboardState.IsKeyDown(Keys.Left) || keyboardState.IsKeyDown(Keys.A))
            {
                movement = -1.0f;
            }
            else if (keyboardState.IsKeyDown(Keys.Right) || keyboardState.IsKeyDown(Keys.D))
            {
                movement = 1.0f;
            }

            // Check if the player wants to jump.
            isJumping = keyboardState.IsKeyDown(Keys.Space) ||
                        keyboardState.IsKeyDown(Keys.Up) ||
                        keyboardState.IsKeyDown(Keys.W);
        }

        public void ApplyPhysics(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector2 previousPosition = Position;

            velocity.X += movement * MoveAcceleration * elapsed;
            velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, 
                -MaxFallSpeed, MaxFallSpeed);

            if (Position.Y > 450.0f)
            {
                velocity.Y = DoJump(velocity.Y, gameTime);
            }

            // Apply pseudo-drag horizontally.
            if (IsOnGround)
            {
                velocity.X *= GroundDragFactor;
            }
            else
            {
                velocity.X *= AirDragFactor;
            }

            // Prevent the player from running faster than his top speed.            
            velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);

            // Apply velocity.
            Position += velocity * elapsed;
            Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));

            //------------------------------------------------------------------------
            // If the player is now colliding with the level, separate them.
            if (Position.Y > 540.0f && !isJumping)
            {
                previousPosition.Y = 540.0f;

                Position += velocity * elapsed;
                Position = new Vector2((float)Math.Round(Position.X), 
                    (float)Math.Round(previousPosition.Y));
            }

            //------------------------------------------------------------------------

            // If the collision stopped us from moving, reset the velocity to zero.
            if (Position.X == previousPosition.X)
            {
                velocity.X = 0;
            }

            if (Position.Y == previousPosition.Y)
            {
                velocity.Y = 0;
            }
        }

        public void OnReachedExit()
        {
            /*kAnimationPlayer.PlayAnimation(kFinishingLine);*/
        }

        public void OnKilled(Enemy killedBy)
        {
            isAlive = false;

            if (killedBy != null)
            {
                //killedSound.Play();
            }
            else
            {
                //fallSound.Play();
            }

            kAnimationPlayer.PlayAnimation(kDying);
        }

        private float DoJump(float velocityY, GameTime gameTime)
        {
            // If the player wants to jump
            if (isJumping)
            {
                // Begin or continue a jump
                IsOnGround = true;
                if ((!wasJumping && IsOnGround) || jumpTime > 0.0f)
                {
                    if (jumpTime == 0.0f)
                    {
                        //jumpSound.Play();
                    }

                    jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
                    kAnimationPlayer.PlayAnimation(kJumping);
                }

                // If we are in the ascent of the jump
                if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
                {
                    IsOnGround = false;
                    // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
                    velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
                }
                else
                {
                    IsOnGround = false;
                    // Reached the apex of the jump
                    jumpTime = 0.0f;
                }
            }
            else
            {
                isOnGround = true;
                // Continues not jumping or cancels a jump in progress
                jumpTime = 0.0f;
            }

            wasJumping = isJumping;

            return velocityY;
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // Flip the sprite to face the way we are moving.
            if (Velocity.X < 0)
            {
                kFlipSprite = SpriteEffects.FlipHorizontally;
            }
            else if (Velocity.X > 0)
            {
                kFlipSprite = SpriteEffects.None;
            }

            // Draw that sprite.
            kAnimationPlayer.Draw(gameTime, spriteBatch, Position, kFlipSprite);
        }

        public void Update(GameTime gameTime)
        {
            GetInput();

            ApplyPhysics(gameTime);

            if (IsAlive && IsOnGround)
            {
                if (Math.Abs(Velocity.X) - 0.02f > 0)
                {
                    kAnimationPlayer.PlayAnimation(kWalking);
                }
                else
                {
                    kAnimationPlayer.PlayAnimation(kIdle);
                }
            }

            // Clear input.
            movement = 0.0f;
            isJumping = false;
        }
    }
}
